/**
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/LICENSE
 */

import { Directionality } from '@angular/cdk/bidi';
import { ComponentType, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
import { Injectable, Injector, OnDestroy, TemplateRef, inject } from '@angular/core';
import { Observable, Subject, defer } from 'rxjs';
import { startWith } from 'rxjs/operators';

import { NzConfigService } from 'ng-zorro-antd/core/config';
import { warn } from 'ng-zorro-antd/core/logger';
import { overlayZIndexSetter } from 'ng-zorro-antd/core/overlay';
import { IndexableObject, NzSafeAny } from 'ng-zorro-antd/core/types';
import { isNotNil } from 'ng-zorro-antd/core/util';

import { MODAL_MASK_CLASS_NAME, NZ_CONFIG_MODULE_NAME, NZ_MODAL_DATA } from './modal-config';
import { NzModalConfirmContainerComponent } from './modal-confirm-container.component';
import { NzModalContainerComponent } from './modal-container.component';
import { BaseModalContainerComponent } from './modal-container.directive';
import { NzModalRef } from './modal-ref';
import { ConfirmType, ModalOptions } from './modal-types';
import { applyConfigDefaults, getValueWithConfig } from './utils';

type ContentType<T> = ComponentType<T> | TemplateRef<T> | string;

@Injectable()
export class NzModalService implements OnDestroy {
  // 当前层级打开的模态框数组
  private openModalsAtThisLevel: NzModalRef[] = [];
  // 当前层级所有模态框关闭后的subject
  private readonly afterAllClosedAtThisLevel = new Subject<void>();

  // 获取当前打开的模态框
  get openModals(): NzModalRef[] {
    // 如果存在父模态框，则获取父模态框的打开模态框
    return this.parentModal ? this.parentModal.openModals : this.openModalsAtThisLevel;
  }

  /**
   * 获取_afterAllClosed属性
   * @returns {Subject<void>} 返回_afterAllClosed属性
   */
  get _afterAllClosed(): Subject<void> {
    const parent = this.parentModal;
    return parent ? parent._afterAllClosed : this.afterAllClosedAtThisLevel;
  }

  // 定义一个只读的afterAllClose属性，类型为Observable<void>
  readonly afterAllClose: Observable<void> = defer(() =>
    // 如果打开的模态框数量大于0，则返回_afterAllClosed，否则返回_afterAllClosed.pipe(startWith(undefined))
    this.openModals.length ? this._afterAllClosed : this._afterAllClosed.pipe(startWith(undefined))
  ) as Observable<void>;

  private parentModal = inject(NzModalService, { skipSelf: true, optional: true });

  // 构造函数，接收四个参数
  constructor(
    // 私有属性overlay，类型为Overlay
    private overlay: Overlay,
    // 私有属性injector，类型为Injector
    private injector: Injector,
    // 私有属性nzConfigService，类型为NzConfigService
    private nzConfigService: NzConfigService,
    // 私有属性directionality，类型为Directionality
    private directionality: Directionality
  ) { }

  // 创建一个模态框，并返回模态框的引用
  create<T, D = NzSafeAny, R = NzSafeAny>(config: ModalOptions<T, D, R>): NzModalRef<T, R> {
    // 将传入的config参数中的nzContent属性转换为ComponentType<T>类型
    return this.open<T, D, R>(config.nzContent as ComponentType<T>, config);
  }

  closeAll(): void {
    // 关闭所有打开的模态框
    this.closeModals(this.openModals);
  }

  /**
   * 创建一个确认模态框
   * @param options 模态框选项
   * @param confirmType 确认类型
   * @returns 模态框实例
   */
  confirm<T>(options: ModalOptions<T> = {}, confirmType: ConfirmType = 'confirm'): NzModalRef<T> {
    // 如果options中包含nzFooter，则警告，因为确认模态框不支持nzFooter属性
    if ('nzFooter' in options) {
      warn(`The Confirm-Modal doesn't support "nzFooter", this property will be ignored.`);
    }
    // 如果options中不包含nzWidth，则设置为416
    if (!('nzWidth' in options)) {
      options.nzWidth = 416;
    }
    // 如果options中不包含nzMaskClosable，则设置为false
    if (!('nzMaskClosable' in options)) {
      options.nzMaskClosable = false;
    }

    // 设置模态框类型为confirm
    options.nzModalType = 'confirm';
    // 设置模态框类名为ant-modal-confirm和ant-modal-confirm-confirmType，以及用户传入的类名
    options.nzClassName = `ant-modal-confirm ant-modal-confirm-${confirmType} ${options.nzClassName || ''}`;
    // 创建模态框实例
    return this.create(options);
  }

  info<T>(options: ModalOptions<T> = {}): NzModalRef<T> {
    // 调用confirmFactory方法，传入options和'info'，返回NzModalRef<T>
    return this.confirmFactory(options, 'info');
  }

  /**
   * 创建一个成功模态框
   * @param options 模态框选项
   * @return 模态框实例
   */
  success<T>(options: ModalOptions<T> = {}): NzModalRef<T> {
    return this.confirmFactory(options, 'success');
  }

  error<T>(options: ModalOptions<T> = {}): NzModalRef<T> {
    return this.confirmFactory(options, 'error');
  }

  warning<T>(options: ModalOptions<T> = {}): NzModalRef<T> {
    return this.confirmFactory(options, 'warning');
  }

  // 打开一个模态框，并返回模态框的引用
  private open<T, D, R>(componentOrTemplateRef: ContentType<T>, config?: ModalOptions<T, D, R>): NzModalRef<T, R> {
    // 将传入的配置对象应用默认值
    const configMerged = applyConfigDefaults(config || {}, new ModalOptions());
    // 创建一个遮罩层
    const overlayRef = this.createOverlay(configMerged);
    // 将模态框附加到遮罩层上
    const modalContainer = this.attachModalContainer(overlayRef, configMerged);
    // 将模态框的内容附加到模态框上
    const modalRef = this.attachModalContent<T, D, R>(componentOrTemplateRef, modalContainer, overlayRef, configMerged);
    // 将模态框的引用赋值给模态框容器
    modalContainer.modalRef = modalRef;

    // 设置遮罩层的z-index
    overlayZIndexSetter(overlayRef, config?.nzZIndex);

    // 将当前打开的模态框添加到打开的模态框数组中
    this.openModals.push(modalRef);
    // 订阅模态框关闭事件，关闭后将当前模态框从打开的模态框数组中移除
    modalRef.afterClose.subscribe(() => this.removeOpenModal(modalRef));

    // 返回模态框的引用
    return modalRef;
  }

  private removeOpenModal(modalRef: NzModalRef): void {
    const index = this.openModals.indexOf(modalRef);
    if (index > -1) {
      this.openModals.splice(index, 1);

      if (!this.openModals.length) {
        this._afterAllClosed.next();
      }
    }
  }

  private closeModals(dialogs: NzModalRef[]): void {
    let i = dialogs.length;
    while (i--) {
      dialogs[i].close();
      if (!this.openModals.length) {
        this._afterAllClosed.next();
      }
    }
  }

  // 创建一个遮罩层
  private createOverlay(config: ModalOptions): OverlayRef {
    // 获取全局配置
    const globalConfig: NzSafeAny = this.nzConfigService.getConfigForComponent(NZ_CONFIG_MODULE_NAME) || {};
    // 创建遮罩层配置
    const overlayConfig = new OverlayConfig({
      // 是否有背景
      hasBackdrop: true,
      // 滚动策略
      scrollStrategy: this.overlay.scrollStrategies.block(),
      // 定位策略
      positionStrategy: this.overlay.position().global(),
      // 导航时是否关闭
      disposeOnNavigation: getValueWithConfig(config.nzCloseOnNavigation, globalConfig.nzCloseOnNavigation, true),
      // 方向
      direction: getValueWithConfig(config.nzDirection, globalConfig.nzDirection, this.directionality.value)
    });
    // 如果有背景
    if (getValueWithConfig(config.nzMask, globalConfig.nzMask, true)) {
      // 设置背景类名
      overlayConfig.backdropClass = MODAL_MASK_CLASS_NAME;
    }

    // 创建遮罩层
    return this.overlay.create(overlayConfig);
  }

  private attachModalContainer(overlayRef: OverlayRef, config: ModalOptions): BaseModalContainerComponent {
    // 获取用户注入的injector
    const userInjector = config && config.nzViewContainerRef && config.nzViewContainerRef.injector;
    // 创建一个新的injector
    const injector = Injector.create({
      parent: userInjector || this.injector,
      providers: [
        { provide: OverlayRef, useValue: overlayRef },
        { provide: ModalOptions, useValue: config }
      ]
    });

    // 根据配置的nzModalType，确定使用哪个容器组件
    const ContainerComponent =
      config.nzModalType === 'confirm' ? NzModalConfirmContainerComponent : NzModalContainerComponent;

    // 创建一个ComponentPortal
    const containerPortal = new ComponentPortal<BaseModalContainerComponent>(
      ContainerComponent,
      config.nzViewContainerRef,
      injector
    );
    // 将ComponentPortal附加到overlayRef上
    const containerRef = overlayRef.attach<BaseModalContainerComponent>(containerPortal);

    // 返回容器组件实例
    return containerRef.instance;
  }

  // 创建一个NzModalRef实例，并传入参数：overlayRef、config、modalContainer
  private attachModalContent<T, D, R>(
    componentOrTemplateRef: ContentType<T>,
    modalContainer: BaseModalContainerComponent,
    overlayRef: OverlayRef,
    config: ModalOptions<T>
  ): NzModalRef<T, R> {
    const modalRef = new NzModalRef<T, R>(overlayRef, config, modalContainer);

    // 如果componentOrTemplateRef是TemplateRef类型，则将TemplatePortal实例附加到modalContainer
    if (componentOrTemplateRef instanceof TemplateRef) {
      modalContainer.attachTemplatePortal(
        new TemplatePortal<T>(componentOrTemplateRef, null!, {
          $implicit: config.nzData,
          modalRef
        } as NzSafeAny)
      );
      // 如果componentOrTemplateRef不为空，且不是字符串类型，则创建一个Injector，并将ComponentPortal实例附加到modalContainer
    } else if (isNotNil(componentOrTemplateRef) && typeof componentOrTemplateRef !== 'string') {
      const injector = this.createInjector<T, D, R>(modalRef, config);
      const contentRef = modalContainer.attachComponentPortal<T>(
        new ComponentPortal(componentOrTemplateRef, config.nzViewContainerRef, injector)
      );
      modalRef.componentRef = contentRef;
      modalRef.componentInstance = contentRef.instance;
      // 否则将字符串类型附加到modalContainer
    } else {
      modalContainer.attachStringContent();
    }
    return modalRef;
  }

  // 创建一个注入器
  private createInjector<T, D, R>(modalRef: NzModalRef<T, R>, config: ModalOptions<T, D, R>): Injector {
    // 获取用户自定义的注入器
    const userInjector = config && config.nzViewContainerRef && config.nzViewContainerRef.injector;

    // 创建一个新的注入器
    return Injector.create({
      parent: userInjector || this.injector,
      providers: [
        { provide: NzModalRef, useValue: modalRef },
        { provide: NZ_MODAL_DATA, useValue: config.nzData }
      ]
    });
  }

  // 定义一个私有函数confirmFactory，接收两个参数：options和confirmType，返回一个NzModalRef<T>类型的值
  private confirmFactory<T>(options: ModalOptions<T> = {}, confirmType: ConfirmType): NzModalRef<T> {
    // 定义一个iconMap对象，用于存储不同confirmType对应的icon
    const iconMap: IndexableObject = {
      info: 'info-circle',
      success: 'check-circle',
      error: 'close-circle',
      warning: 'exclamation-circle'
    };
    // 如果options中没有nzIconType属性，则将其设置为iconMap中对应的值
    if (!('nzIconType' in options)) {
      options.nzIconType = iconMap[confirmType];
    }
    // 如果options中没有nzCancelText属性，则将其设置为null
    if (!('nzCancelText' in options)) {
      options.nzCancelText = null;
    }
    // 调用confirm函数，传入options和confirmType，返回一个NzModalRef<T>类型的值
    return this.confirm(options, confirmType);
  }

  /**
 * 当组件销毁时调用
 */
  ngOnDestroy(): void {
    // 关闭所有打开的模态框
    this.closeModals(this.openModalsAtThisLevel);
    // 完成所有关闭模态框的操作
    this.afterAllClosedAtThisLevel.complete();
  }
}
